package org.performancetracker.jdbc4;

import org.performancetracker.api.Aggregator;
import org.performancetracker.api.Tracker;
import org.performancetracker.TrackerFactory;

import java.sql.*;
import java.util.Properties;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: jaronschut
 */

public class MonitoringDriver implements Driver {
    private static final String LOG_DRIVER_CREATION_1 = "Created driver '";
    private static final String LOG_DRIVER_CREATION_2 = "' for url '";
    private static final String LOG_DRIVER_CREATION_3 = "'";

    private static final String CONFIG_AGGRAGATOR_NAME = "aggregatorName";
    private static final String DEFAULT_AGGREGATOR_NAME = "Tracker.Jdbc";

    private static String AGGREGATOR_NAME = DEFAULT_AGGREGATOR_NAME;
    private static Aggregator TRACKER_AGGREGATOR;

    private final ConcurrentMap<String, Driver> driverMap = new ConcurrentHashMap<String, Driver>();
    private volatile Driver currentDriver = null;

    /**
     * Useless constructor. 
     */
    public MonitoringDriver() {}


    private Driver getRealDriver(String url) throws SQLException {
             return driverMap.get(url);
    }

    /**
     * Get the driver from the cache or create it.
     *
     * @param url
     * @return
     */
    private Driver getOrCreateRealDriver(String url) throws SQLException {
        assert(url != null);

        Driver driver = null;

        while ((driver = driverMap.get(url)) == null) {
            driver = this.createRealDriver(url);
            driverMap.putIfAbsent(url, driver);
        }

        currentDriver = driver;
        // This is wrong in multithreaded enviroenment
        // TODO: fix this shit

        return driver;
    }

    /**
     * Get the driver from the DriverManager.
     *
     * @param url
     * @return
     * @throws SQLException
     */
    private Driver createRealDriver(String url) throws SQLException {

        Tracker tracker = TrackerFactory.startTracker();
        Driver driver = null;

        try {
            Enumeration drivers = DriverManager.getDrivers();

            while (drivers.hasMoreElements()) {
                driver = (Driver) drivers.nextElement();

                if (driver.acceptsURL(url)) {
                    return driver;
                }
            }
        } finally {
            tracker.stop().aggregate(LOG_DRIVER_CREATION_1
                + driver.getClass().getCanonicalName()
                + LOG_DRIVER_CREATION_2
                + url
                + LOG_DRIVER_CREATION_3);
        }

        return null;
    }

    /**
     * Implementing the getConnection and wrapping the connection with monitoring wrapper.
     *
     * @param url
     * @param info
     * @return
     * @throws SQLException
     */
    public Connection connect(String url, Properties info) throws SQLException {

        Driver realDriver = this.getOrCreateRealDriver(url);

        Connection realConnection = realDriver.connect(url, info);

        MonitoringConnection monitoringConnection = new MonitoringConnection(realConnection);

        return monitoringConnection;
    }

    /**
     * Implementing acceptsUrl and passing it through to the real driver.
     *
     * @param url
     * @return
     * @throws SQLException
     */
    public boolean acceptsURL(String url) throws SQLException {

        Driver realDriver = this.getOrCreateRealDriver(url);

        return realDriver.acceptsURL(url);
    }

    /**
     * Implementing getPropertyInfo and passing it through to the real driver.
     *
     * @param url
     * @param info
     * @return
     * @throws SQLException
     */
    public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {

        Driver realDriver = this.getOrCreateRealDriver(url);

        return realDriver.getPropertyInfo(url, info);
    }

    /**
     * Pass through to last used realDriver.
     *
     * @return
     */
    public int getMajorVersion() {
        return currentDriver == null ? 0 : currentDriver.getMajorVersion();
    }

    /**
     * Pass through to last used realDriver.
     *
     * @return
     */
    public int getMinorVersion() {
        return currentDriver == null ? 0 : currentDriver.getMinorVersion();
    }

    /**
     * Pass through to last used realDriver.
     *
     * @return
     */
    public boolean jdbcCompliant() {
        return currentDriver == null ? true : currentDriver.jdbcCompliant();
    }
}
